#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <algorithm>
#include "include/TcpSocket.hpp"
#include "include/Log.hpp"
#include "Epoller.hpp"

const static int default_port = 9090;
const static int default_maxevents = 64;

std::string eventToString(uint32_t events)
{
    std::string info;
    if(events & EPOLLIN) info += "EPOLLIN ";
    if(events & EPOLLOUT) info += "EPOLLOUT ";
    if(events & EPOLLET) info += "EPOLLET ";
    return info;
}

class EpollServer
{
public:
    EpollServer(uint16_t port = default_port)
    : _port(port), _listen_sock(new TcpSocket())
    , _isRunning(false), _epoller(new Epoller())
    {
        _listen_sock->createListenSocket(_port);
        log.message(INFO, "listen socket init success, socket fd: %d", _listen_sock->getSocketFd());
        _epoller->addEvent(_listen_sock->getSocketFd(), EPOLLIN);
    }

    void start()
    {
        _isRunning = true;
        while(true)
        {
            int timeout = 10000;
            int n = _epoller->wait(_revs, default_maxevents, timeout);
            if(n == -1)
            {
                log.message(ERROR, "epoll wait error...");
            }
            else if(n == 0)
            {
                log.message(INFO, "epoll wait timeout...");
            }
            else
            {
                eventHandler(n);
                log.message(INFO, "event happend...");
            }
        }
        _isRunning = false;
    }

    ~EpollServer()
    {}
private:
    void eventHandler(int events_num)
    {
        for(int i = 0; i < events_num; i++)
        {
            int fd = _revs[i].data.fd; //ev.data.fd = sockfd;
            uint32_t events = _revs[i].events;
            log.message(DEBUG, "fd: %d, event: %s , is ready...", fd, eventToString(events).c_str());
            if(events & EPOLLIN) //读事件
            {
                if(fd == _listen_sock->getSocketFd()) //连接到来
                {
                    std::string clientIp;
                    uint16_t clientPort;
                    Socket* connect_sock = _listen_sock->accept(&clientIp, &clientPort);
                    if(connect_sock == nullptr)
                    {
                        log.message(ERROR, "accept error...");
                        continue;
                    }

                    _epoller->addEvent(connect_sock->getSocketFd(), EPOLLIN);
                    log.message(INFO, "[%s:%d], new client is connected...", clientIp.c_str(), clientPort);
                }
                else //数据到来
                {
                    char buffer[1024];
                    ssize_t n = read(fd, buffer, sizeof(buffer)-1);
                    if(n > 0)
                    {
                        buffer[n] = '\0';
                        std::cout << "echo# " << buffer << std::endl;
                    }
                    else
                    {
                        _epoller->deletEvent(fd); //把关心的fd从epoll模型红黑树中移除
                        close(fd); //先移除再关闭
                        log.message(INFO, "client close or read error... fd: %d", fd);
                    }
                }
            }
        }
    }
private:
    uint16_t _port;
    std::unique_ptr<Socket> _listen_sock;
    std::unique_ptr<Epoller> _epoller;
    struct epoll_event _revs[default_maxevents];
    bool _isRunning;
};